Create a Report with ZK using iReport and JasperReports
Jimmy Shiau & Vincent Jian, Engineer, Potix Corporation
April 19, 2012
ZK 5/6, iReport 4.5.0, JasperReports 4.5.0
Introduction
JasperReports is a powerful reporting engine which supports various formats including pdf, html, rtf, xls, doc etc, ZK has integrated JasperReports to leverage these powers for ease of reporting. This article demonstrates how you can export your ZK-powered page to a desired JasperReport format and layout, designed by iReport, a GUI based design tool for JasperReport.
Prerequisite
JasperReports
ZK 5.0.8/6 and later have integrated jasperreport version 4.0.1 and you can use the jasperreports.jar packaged within ZK directly. If you want to use the latest version of jasperreport, you can download it from http://sourceforge.net/projects/jasperreports/files/jasperreports/ Note that ZK JasperReport Integration is part of ZK PE/EE so please download ZK PE/EE or the evaluation copy of ZK PE/EE.
iReport
Download iReport from JasperForge and install. Notice that the version of iReport should exactly match with the version of JasperReports.
Scenario One: Create Report with Custom Data Source
In this sample, we assume you have a ZK page with two grids which contain custom data source and you wish to export these grids to a PDF/RTF table using JasperReports.
The ZK page
The data page we demonstrate here is a Food List with various categories. The list is displayed as two grid components.
Necessary classes are as follows:
- Food bean.
- Food Data Access Object.
- Data set by model and render by RowRenderer.
public class Food {
private String Category;
private String Name;
private String TopNutrients;
private Integer DailyPercent;
private Integer Calories;
private String Quantity;
public Food(String category, String name, String topNutrients, Integer dailyPercent, Integer calories, String quantity) {
Category = category;
Name = name;
TopNutrients = topNutrients;
DailyPercent = dailyPercent;
Calories = calories;
Quantity = quantity;
}
// getters and setters
}
public class FoodData {
private static List<Food> foods = new ArrayList<Food>();
static {
foods.add(new Food("Vegetables", "Asparagus", "Vitamin K", 115, 43, "1 cup - 92 grams"));
foods.add(new Food("Vegetables", "Beets", "Folate", 33, 74, "1 cup - 170 grams"));
// other foods
}
public static List<Food> getFoodsByCategory(String category) {
List<Food> somefoods = new ArrayList<Food>();
for (Iterator<Food> i = foods.iterator(); i.hasNext();) {
Food tmp = i.next();
if (tmp.getCategory().equals(category))
somefoods.add(tmp);
}
return somefoods;
}
}
public class ReportSample1Composer extends GenericForwardComposer {
private Grid vegetable;
private Grid seafood;
private List<Food> vegetables = FoodData.getFoodsByCategory("Vegetables");
private List<Food> seafoods = FoodData.getFoodsByCategory("Seafood");
public void doAfterCompose(Component comp) throws Exception {
super.doAfterCompose(comp);
RowRenderer renderer = new RowRenderer() {
public void render(Row row, Object data) throws Exception {
Food food = (Food) data;
new Label(food.getName()).setParent(row);
new Label(food.getTopNutrients()).setParent(row);
new Label(food.getDailyPercent() + "").setParent(row);
new Label(food.getCalories() + "").setParent(row);
new Label(food.getQuantity()).setParent(row);
}
};
vegetable.setModel(new ListModelList(vegetables));
vegetable.setRowRenderer(renderer);
seafood.setModel(new ListModelList(seafoods));
seafood.setRowRenderer(renderer);
}
}
Design the report layout by iReport
Now we are ready to design our report with the following steps:
- First, execute iReport and set classpath that contains the Food class
- Then, create a new empty report with Blank A4 template and change the language properties to Java
- If you want to specify report title dynamically in Java code instead of static text, you can add a parameter named "reportTitle" and specify "Parameter class" to
java.lang.String
- Add two parameters named "vegetableDataSource" and "seafoodDataSource", then specify "Parameter class" of both parameters to
net.sf.jasperreports.engine.JRDataSource
- Add an empty data set in the report named "FoodDataSet"
- Now, design the report layout for vegetable grid in the ZUL page
- Add fields in "FoodDataSet" according to Food bean properties
- Add "Table" element to "Detail 1" band and follow "Table Wizard"
- Create a table from "FoodDataSet" and set columns to "5", then click "Next"
- Select the fields you want to show in the table, then click "Next"
- Select "Use a JRDatasource expression" option and type
$P{vegetableDataSource}
in the expression, then click "Next" - Design the table style, then click "Finish"
- Tweak the report and table layout as you wish
- Then, design the report layout for seafood grid in the ZUL page
- Add another Detail band in the report
- Repeat step 6-2 to add another table in "Detail 2" band
- Compile MyReport and copy MyReport.jasper file to your web project
Use ZK JasperReports in your web project
Now we need to create data source instance and set ZK JasperReports settings as follows:
- Create a
DataSource
instance that implementsnet.sf.jasperreports.engine.JRDataSource
- Create a report.zul page which contains jasperreport component
- Create two buttons in the index.zul page, and let them forward onClick event with a string to the onShowReport event in the composer.
- Now we have finished all the settings, the following image shows what the resulting report looks like:
public class DataSource implements JRDataSource {
private List<Food> foods;
private int index = -1;
public DataSource(List<Food> foods) {
super();
this.foods = foods;
}
public Object getFieldValue(JRField field) throws JRException {
String fieldName = field.getName();
Food food = foods.get(index);
if ("name".equals(fieldName)) {
return food.getName();
} else if ("topNutrients".equals(fieldName)) {
return food.getTopNutrients();
} else if ("dailyPercent".equals(fieldName)) {
return food.getDailyPercent();
} else if ("calories".equals(fieldName)) {
return food.getCalories();
} else if ("quantity".equals(fieldName)) {
return food.getQuantity();
}
return "";
}
public boolean next() throws JRException {
return ++index < foods.size();
}
}
<window title="Report" border="normal" width="800px" height="600px" closable="true" mode="modal">
<jasperreport id="report" height="600px" />
</window>
<hlayout>
Export Report to <button label="PDF" forward='onShowReport(pdf)' /><button label="RTF" forward='onShowReport(rtf)' />
</hlayout>
public void onShowReport(ForwardEvent event) {
String type = event.getData().toString();
String path = page.getDesktop().getWebApp().getRealPath("/sample1");
Window win = (Window) Executions.createComponents("report.zul", null, null);
Map<String, Object> params = new HashMap<String, Object>();
// Add parameters used in this report
params.put("reportTitle", "My Food List");
params.put("vegetableDataSource", new DataSource(vegetables));
params.put("seafoodDataSource", new DataSource(seafoods));
Jasperreport report = (Jasperreport) win.getFellow("report");
report.setType(type);
report.setSrc(path + "/MyReport.jasper");
report.setParameters(params);
}
Scenario Two: Create Sub-Report with Bean Collection Data Source
In this sample, we assume you have a ZK page with a Master-Detail grid which contains a bean collection data source where each bean has another bean collection data. And you wish to export the grid to a PDF/RTF sub-report table using JasperReports.
The ZK page
The data page we demonstrate here contains a Person List with each person's favorite Food list. The list is displayed as a Master-Detail grid component.
Necessary classes are as follows:
- Person bean with a list of Food bean in scenario one.
- Person Data Access Object.
- Use data binding to show the data page in index.zul page.
public class Person {
private String name;
private Integer age;
private List<Food> foods = new ArrayList<Food>();
public Person(String name, Integer age, List<Food> foods) {
this.name = name;
this.age = age;
this.foods = foods;
}
// getters and setters
}
public class PersonData {
private static List<Person> people = new ArrayList<Person>();
static {
people.add(new Person("Tom Riddle", 50, FoodData.getFoodsByCategory("Grains")));
people.add(new Person("Harry Potter", 20, FoodData.getFoodsByCategory("Fruits")));
people.add(new Person("Ron Wesley", 21, FoodData.getFoodsByCategory("Seafood")));
}
public static List<Person> getPeople() {
return people;
}
}
<?init class="org.zkoss.zkplus.databind.AnnotateDataBinderInit" ?>
<zk>
<window id="win" title="JasperReports Sample 1" border="normal" apply="web.ui.ctrl.ReportSample2Composer">
<grid id="people" model="@{win$composer.personList}">
<columns>
<column width="40px" />
<column label="Name" />
<column label="Age" />
</columns>
<rows>
<row self="@{each='person'}" value="@{person}">
<detail open="true">
<grid id="vegetable" model="@{person.foods}">
<columns>
<column label="cagetory" />
<column label="Name" />
<!-- other Food properties -->
</columns>
<rows>
<row self="@{each='food'}" value="@{food}">
<label value="@{food.category}"/>
<label value="@{food.name}" />
<!-- other Food properties -->
</row>
</rows>
</grid>
</detail>
<label value="@{person.name}" />
<label value="@{person.age}" />
</row>
</rows>
</grid>
</window>
</zk>
Design the report layout by iReport
Now we are ready to design our report with the following steps:
- Execute iReport and set classpath that contains Person.class and Food.class
- Create a new report by iReport
- Set data source of the report
- Design the report and drag all Fields in Detail band
- Remove Field value "foods" as the food bean data collection will be listed in sub-report
- Adding sub-report element in Detail band will trigger iReport wizard to create a new report
- Press next directly in the first 5 steps of the wizard, and in step 6 of the wizard, remember to select "Store the directory name in a parameter" option.
- In step 7 of the wizard, choose "Use a JRDataSource expression" option and click the edit icon
- Type
new net.sf.jasperreports.engine.data.JRBeanCollectionDataSource($F{foods})
in Expression editor then click Apply - Repeat step 2-1 to set data source of sub-report with
Food.class
bean and design the sub-report style. - Edit "Default Value Expressions" attribute of the parameter
SUBREPORT_DIR
in main-report using an empty string "" for web project - Compile report and copy both *.jasper files to your web project Right-Click MainReport and execute "Compile Report" to compile the report. It will also compile sub-report automatically.
- Create a report.zul page contains jasperreport component
- Create two buttons in the index.zul page, and let them forward onClick event with a string to the onShowReport event in the composer.
- Result
- To export your report to a different format other than PDF and RTF, please refer to support type list.
- To embed different fonts in the report, please refer to this blog.
Use ZK JasperReports in your web project
Now we can set ZK JasperReports settings as follows:
<window title="Report" border="normal" width="800px" height="600px" closable="true" mode="modal">
<jasperreport id="report" height="600px" />
</window>
<hlayout>
Export Report to <button label="PDF" forward='onShowReport(pdf)' /><button label="RTF" forward='onShowReport(rtf)' />
</hlayout>
public void onShowReport(ForwardEvent event) {
String type = event.getData().toString();
// get the absolute path that contains *.jasper files
String path = page.getDesktop().getWebApp().getRealPath("/sample2");
Window win = (Window) Executions.createComponents("report.zul", null, null);
// set parameters used in the main-report
Map<String, Object> params = new HashMap<String, Object>();
params.put("SUBREPORT_DIR", path + "/");
// set jasperreport component attributes
Jasperreport report = (Jasperreport) win.getFellow("report");
report.setType(type);
report.setSrc(path + "/MainReport.jasper");
report.setParameters(params);
report.setDatasource(new JRBeanCollectionDataSource(PersonData.getPeople()));
}
See Also
Download
Download the source code and war file of the two samples illustrated above at: jasperreport.zip
Comments
Copyright © Potix Corporation. This article is licensed under GNU Free Documentation License. |